{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ['CUDA_VISIBLE_DEVICES'] = '0'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/husein/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:516: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:517: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:518: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:519: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:520: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:525: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n",
      "/usr/lib/python3/dist-packages/requests/__init__.py:80: RequestsDependencyWarning: urllib3 (1.25.3) or chardet (3.0.4) doesn't match a supported version!\n",
      "  RequestsDependencyWarning)\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:541: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:542: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:543: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:544: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:545: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n",
      "/home/husein/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:550: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n"
     ]
    }
   ],
   "source": [
    "import librosa\n",
    "import tensorflow as tf\n",
    "import glob\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "train = glob.glob('spectrogram-train/*.npy')\n",
    "x = []\n",
    "for fpath in train:\n",
    "    fpath = train[-1].split('/')[1]\n",
    "    splitted = fpath.split('-')\n",
    "    if len(splitted) == 2:\n",
    "        splitted[1] = splitted[1].split('.')[1]\n",
    "        fpath = splitted[0] + '.' + splitted[1]\n",
    "    fpath = fpath.replace('.npy','.wav')\n",
    "    x.append('data/' + fpath)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "augment = glob.glob('augment/*.wav')\n",
    "x.extend(augment)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_ = glob.glob('spectrogram-test/*.npy')\n",
    "test = []\n",
    "for t in test_:\n",
    "    f = t.split('/')[1].replace('.npy', '.wav')\n",
    "    test.append('data/'+f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  2%|▏         | 501/29524 [00:32<30:28, 15.87it/s] "
     ]
    }
   ],
   "source": [
    "from tqdm import tqdm\n",
    "\n",
    "X = []\n",
    "for i in tqdm(range(len(x))):\n",
    "    y, sr = librosa.load(x[i], sr = 16000)\n",
    "    X.append(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "features = [(512, 10, 5), (512, 8, 4), (512, 4, 2), (512, 4, 2), (512, 4, 2), (512, 1, 1), (512, 1, 1)]\n",
    "aggs = [(512, 2, 1), (512, 3, 1), (512, 4, 1), (512, 5, 1), (512, 6, 1), (512, 7, 1), (512, 8, 1), (512, 9, 1), \n",
    " (512, 10, 1), (512, 11, 1), (512, 12, 1), (512, 13, 1)]\n",
    "num_negatives = 10\n",
    "prediction_steps = 12\n",
    "learning_rate = 1e-5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "def layer_norm(inputs, epsilon=1e-8):\n",
    "    mean, variance = tf.nn.moments(inputs, [-1], keep_dims=True)\n",
    "    normalized = (inputs - mean) / (tf.sqrt(variance + epsilon))\n",
    "    params_shape = inputs.get_shape()[-1:]\n",
    "    gamma = tf.get_variable('gamma', params_shape, tf.float32, tf.ones_initializer())\n",
    "    beta = tf.get_variable('beta', params_shape, tf.float32, tf.zeros_initializer())\n",
    "    return gamma * normalized + beta\n",
    "\n",
    "\n",
    "def cnn_block(x, hidden_dim, kernel_size, strides):\n",
    "    x =  tf.layers.conv1d(inputs = x,\n",
    "                          filters = hidden_dim,\n",
    "                          kernel_size = kernel_size,\n",
    "                          strides = strides)\n",
    "    x = layer_norm(x)\n",
    "    x = tf.nn.relu(x)\n",
    "    return x\n",
    "\n",
    "def cnn_aggregator(x, hidden_dim, kernel_size, strides):\n",
    "    ka = kernel_size // 2\n",
    "    kb = ka - 1 if kernel_size % 2 == 0 else ka\n",
    "    pad = tf.zeros([tf.shape(x)[0], kb + ka, hidden_dim])\n",
    "    x =  tf.layers.conv1d(inputs = tf.concat([pad, x], 1),\n",
    "                          filters = hidden_dim,\n",
    "                          kernel_size = kernel_size,\n",
    "                          strides = strides)\n",
    "    x = layer_norm(x)\n",
    "    x = tf.nn.relu(x)\n",
    "    return x\n",
    "\n",
    "def negative_sample(y):\n",
    "    bsz = tf.shape(y)[0]\n",
    "    tsz = tf.shape(y)[1]\n",
    "    fsz = tf.shape(y)[2]\n",
    "    y = tf.transpose(y, [2, 0, 1])\n",
    "    y = tf.reshape(y, (fsz, -1))\n",
    "    neg_idxs = tf.random_uniform((bsz, num_negatives * tsz), minval=0, maxval=tsz, dtype=tf.int32)\n",
    "    neg_idxs = tf.reshape(neg_idxs, [-1])\n",
    "    negs = tf.gather(y, neg_idxs, axis=1)\n",
    "    negs = tf.reshape(negs, (fsz, bsz, num_negatives, tsz))\n",
    "    negs = tf.transpose(negs, [2, 1, 3, 0])\n",
    "    return negs\n",
    "    \n",
    "\n",
    "class Model:\n",
    "    def __init__(self):\n",
    "        self.X = tf.placeholder(tf.float32, (None, None))\n",
    "        feature = tf.expand_dims(self.X, axis = 2)\n",
    "        \n",
    "        for no, f in enumerate(features):\n",
    "            size_layers = f[0]\n",
    "            kernel_size = f[1]\n",
    "            strides = f[2]\n",
    "            with tf.variable_scope('feature_%d'%no):\n",
    "                feature = cnn_block(feature, size_layers, kernel_size, strides)\n",
    "        \n",
    "        x = tf.identity(feature)\n",
    "        for no, f in enumerate(aggs):\n",
    "            size_layers = f[0]\n",
    "            kernel_size = f[1]\n",
    "            strides = f[2]\n",
    "            with tf.variable_scope('agg_%d'%no):\n",
    "                x = cnn_aggregator(x, size_layers, kernel_size, strides)\n",
    "                \n",
    "        jin = 0\n",
    "        rin = 0\n",
    "        for _, k, stride in features:\n",
    "            if rin == 0:\n",
    "                rin = k\n",
    "            rin = rin + (k - 1) * jin\n",
    "            if jin == 0:\n",
    "                jin = stride\n",
    "            else:\n",
    "                jin *= stride\n",
    "        offset = math.ceil(rin / jin)\n",
    "\n",
    "        offset = int(offset)\n",
    "        \n",
    "        self.logits = x # X\n",
    "        self.targets = feature # Y\n",
    "        self.negatives = negative_sample(self.targets)\n",
    "        \n",
    "        y = tf.expand_dims(self.targets, axis = 0)\n",
    "        targets = tf.concat([y, self.negatives], axis = 0)\n",
    "        b = tf.shape(targets)[0]\n",
    "        x = tf.expand_dims(self.logits, axis = -1)\n",
    "        \n",
    "        x = tf.layers.conv2d_transpose(x, prediction_steps, (1, 1))\n",
    "        x = tf.expand_dims(x, axis = 0) \n",
    "        x = tf.tile(x, [b, 1, 1, 1, 1])\n",
    "        \n",
    "        copies = tf.shape(x)[0]\n",
    "        bsz = tf.shape(x)[1]\n",
    "        tsz = tf.shape(x)[2]\n",
    "        dim = tf.shape(x)[3]\n",
    "        steps = tf.shape(x)[4]\n",
    "        self.o = x\n",
    "        self.p = targets\n",
    "        \n",
    "        steps = tf.math.minimum(steps, tsz - offset)\n",
    "        predictions = tf.zeros(bsz * copies * (tsz - offset + 1) * \\\n",
    "                               steps - ((steps + 1) * steps // 2) * copies * bsz)\n",
    "        labels = tf.zeros_like(predictions)\n",
    "        \n",
    "        def body(i, start, end, predictions, labels):\n",
    "            offset_ = i + offset\n",
    "            end = start + (tsz - offset_) * bsz * copies\n",
    "            pos_num = (end - start) // copies\n",
    "            s = tf.reduce_sum((x[:, :, :-offset_, :, i] * targets[:, :, offset_:, :]), axis = 3)\n",
    "            s = tf.reshape(s, [-1])\n",
    "            s = tf.pad(s, [[start, tf.shape(predictions)[0] - (start + tf.shape(s)[0])]])\n",
    "            predictions = tf.add(predictions, s)\n",
    "            l = tf.ones((pos_num))\n",
    "            l = tf.pad(l, [[start, tf.shape(labels)[0] - (start + pos_num)]])\n",
    "            labels = tf.add(labels, l)\n",
    "            return i + 1, end, end, predictions, labels\n",
    "\n",
    "        def condition(i, start, end, predictions, labels):\n",
    "            return i < steps\n",
    "\n",
    "        ranged = tf.Variable(tf.constant(0))\n",
    "        _, _, _, predictions, labels = tf.while_loop(condition, body, [0, 0, 0, predictions, labels])\n",
    "        self.predictions = predictions\n",
    "        self.labels = labels\n",
    "        \n",
    "        self.cost = tf.nn.sigmoid_cross_entropy_with_logits(\n",
    "            labels=self.labels,\n",
    "            logits=self.predictions,\n",
    "        )\n",
    "        self.cost = tf.reduce_mean(self.cost)\n",
    "        self.optimizer = tf.train.AdamOptimizer(learning_rate = learning_rate).minimize(self.cost)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tf.reset_default_graph()\n",
    "sess = tf.InteractiveSession()\n",
    "model = Model()\n",
    "sess.run(tf.global_variables_initializer())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "l, t, o, p, q = sess.run([model.logits, model.targets, model.o, model.predictions, model.labels], \n",
    "                         feed_dict = {model.X: resampled})\n",
    "l.shape, t.shape, o.shape, p.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sess.run(model.cost, feed_dict = {model.X: resampled})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
